package com.yixinsoft.basic.web.aop;

import java.util.List;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import com.yixinsoft.basic.model.manage.system.User;

/**
 * Created by hqy on 2016/06/11
 */
@Component
@Aspect
public class UserCacheAspect extends BaseCacheAspect {
	
	private String idKeyPrefix = "id-";
	private String usernameKeyPrefix = "username-";
	
	public UserCacheAspect() {
		setCacheName("sys-userCache");
	}

	@Pointcut(value = "target(com.yixinsoft.basic.service.manage.system.UserService)")
	private void userServicePointcut() {
	}
	
	@Pointcut(value = "execution(* save(..)) "
			+ "|| execution(* update(..)) "
			+ "|| execution(* changePassword(..))")
	private void cachePutPointcut() {
	}
	
	@Pointcut(value = "(execution(* delete(*))) && args(arg)", argNames = "arg")
	private void cacheEvictPointcut(Object arg) {
	}
	
	@Pointcut(value = "(execution(* findByUsername(*)) " +
            "|| execution(* queryById(*)))")
	private void cacheablePointcut() {
	}
	
	@Pointcut(value = "execution(* updateState(..))")
	private void cacheStatePointcut() {
	}
	
	private String idKey(String id) {
		return idKeyPrefix + id;
	}
	
	private String usernameKey(String username) {
		return usernameKeyPrefix + username;
	}
	
	///增强实现
    @AfterReturning(value = "userServicePointcut() && cachePutPointcut()", returning = "user")
    public void cachePutAdvice(Object user) {
        //put cache
    	
        //put((User) user);
    	
    	//evict(String.valueOf(user));
    	
    	clear();
    }
    
    @AfterReturning(value = "cacheStatePointcut()", returning = "users")
    public void cacheStateAdvice(List<User> users) {
    	for (User user : users) {
    		evict(user);
    	}
    }
	  
	
	@After(value = "userServicePointcut() && cacheEvictPointcut(arg)", argNames = "arg")
	public void cacheEvictAdvice(Object arg) {
		if (arg == null) {
			return;
		}
		if (arg instanceof Long) {
			//only evict id
			evict(String.valueOf(arg));
		}
		if (arg instanceof Long[]) {
			for (Long id : (Long[])arg) {
				evict(String.valueOf(id));
			}
		}
		if (arg instanceof String) {
			//only evict id
			evict(String.valueOf(arg));
		}
		if (arg instanceof String[]) {
			for (String id : (String[])arg) {
				evict(id);
			}
		}
		if (arg instanceof User) {
			evict((User)arg);
		}
	}
	
	@Around(value = "userServicePointcut() && cacheablePointcut()")
	public Object cacheableAdvice(ProceedingJoinPoint pjp) throws Throwable {
		String methodName = pjp.getSignature().getName();
		Object arg = pjp.getArgs().length >= 1 ? pjp.getArgs()[0] : null;
		
		String key = "";
		boolean isIdKey = false;
		if ("queryById".equals(methodName)) {
			key = idKey(String.valueOf(arg));
			isIdKey = true;
		} else if ("findByUsername".equals(methodName)) {
			key = usernameKey((String)arg);
		}
		
		User user = null;
		if (isIdKey == true) {
			user = get(key);
		} else {
			Long id = get(key);
			if (id != null) {
				key = idKey(String.valueOf(id));
				user = get(key);
			}
		}
		
		//cache hit
		if (user != null) {
			return user;
		}
		//cache miss
		user = (User)pjp.proceed();
		
		//put cache
		put(user);
		
		return user;
	}
	
	///cache抽象实现
	public void put(User user) {
		if (user == null) {
			return;
		}
		Long id = user.getId();
		put(usernameKey(user.getUsername()), id);
		put(idKey(String.valueOf(id)), user);
	}
	
	public void evictId(String id) {
		evict(idKey(id));
	}
	
	public void evict(User user) {
		if (user == null) {
			return;
		}
		Long id = user.getId();
		evict(idKey(String.valueOf(id)));
		evict(usernameKey(user.getUsername()));
	}
}
